/* --------------------------------------------------------------------------------
 * PropellentNet: Propeller IDE
 * Copyright 2008 - Jason Wood
 * --------------------------------------------------------------------------------
 * SpinInterpreter.cs
 * code parser.
 * --------------------------------------------------------------------------------
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * --------------------------------------------------------------------------------
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Drawing;
using System.Reflection;

namespace PropellentNet
{
    
    #region Structures
    public struct Location
    {
        public Location(int Line, int Char)
        {
            LineNumber = Line;
            CharIndex = Char;
        }
        public int LineNumber;
        public int CharIndex;
    }
    #endregion Structures
    
    #region Interfaces
        #region ICodeSearchEngine
    public interface ICodeSearchEngine
    {
        string RegexSearchString { get; }
        SpinCodeObject[] Search(string SearchStr, ref string SearchStream, ref int currentPos,ref int currentChar, ref int currentLine);
        Regex SearchEngine { get; }
    }
    #endregion ICodeSearchEngine
        #region ISpinCodeObject
    public interface ISpinCodeObject
    {
        ISpinCodeObject Perent { get; }
        string ObjectText { get; set; }
        Location EditorLocation { get; set; }
    }
    #endregion ISpinCodeObject
        #region IDrawable
    public interface IDrawable
    {
        void Draw(System.Drawing.Graphics Graphics, System.Drawing.SizeF CharSize);
    }
    #endregion IDrawable
        #region ISpinType
    public interface ISpinType
    {
        string TypeName { get; }
        Color TextColor { get; set; }
        Color BackgroundColor { get; set; }
        bool IsBold { get; set; }
    }
    #endregion ISpinType
    #endregion Interfaces

    #region Attribute Class
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
    public class SearchEngineAttribute : System.Attribute
    {

        public readonly string ObjectName;
        
        private string topic;

        public string Topic               // Topic is a named parameter
        {
            get
            {
                return topic;
            }
            set
            {
                topic = value;
            }
        }

        public SearchEngineAttribute(string ObjectName)  // url is a positional parameter
        {
            this.ObjectName = ObjectName;
        }

        public override string ToString()
        {
            return ObjectName;
        }

    }
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
    public class SpinTypeAttribute : System.Attribute
    {

        public readonly string ObjectName;

        private string topic;

        public string Topic               // Topic is a named parameter
        {
            get
            {
                return topic;
            }
            set
            {
                topic = value;
            }
        }

        public SpinTypeAttribute(string ObjectName)  // url is a positional parameter
        {
            this.ObjectName = ObjectName;
        }

        public override string ToString()
        {
            return ObjectName;
        }

    }
    [AttributeUsage(AttributeTargets.All)]
    public class SpinParameterAttribute : System.Attribute
    {

        private string m_ParameterName;

        public SpinParameterAttribute(string ParameterName)
        {
            m_ParameterName = ParameterName;
        }
        
        public override string ToString()
        {
            return m_ParameterName;
        }

    }
    #endregion Attribute Class
    
    #region Abstract Class SpinType's
    public abstract class SpinType : ISpinType
    {
        
        private string m_Regex;
        private string m_RegedEnd;
        private string m_TypeName;
        private bool m_IsBold;
        private Color m_TextColor;
        private Color m_BackgroundColor;
        
        public SpinType(string typeName)
        {
            this.m_TypeName = typeName;
        }
        
        #region ISpinType Members
       
        public string TypeName
        {
            get
            {
                return m_TypeName;
            }
        }
        
        public Color TextColor
        {
            get
            {
                return (Color)Properties.GetProperty(TypeName, "TextColor");
            }
            set
            {
                Properties.SetProperty(TypeName, "TextColor", value);
                //m_TextColor = value;
            }
        }
        
        public Color BackgroundColor
        {
            get
            {
                return m_BackgroundColor;
            }
            set
            {
                m_BackgroundColor = value;
            }
        }
        
        public bool IsBold
        {
            get
            {
                return m_IsBold;
            }
            set
            {
                m_IsBold = value;
            }
        }
        
        #endregion

    }
    #endregion Abstract Class SpinType's
    
    #region Abstract Class SpingCodeObject
    public abstract class SpinCodeObject : SpinType, ISpinCodeObject, IDrawable
    {
        
        #region Member variables
        private ISpinCodeObject m_Parent;
        private Location m_Location;
        private string m_ObjectText;
        #endregion Member variables
        
        #region De/Constructor
        public SpinCodeObject(ISpinCodeObject Parent, string TypeName) : base (TypeName)
        {
            m_Parent = Parent;
            RegisterParameters();
        }
        #endregion constructor
        
        #region ISpinCodeObject Members
        public ISpinCodeObject Perent
        {
            get { return m_Parent; }
        }
        public string ObjectText
        {
            get
            {
                return m_ObjectText;
            }
            set
            {
                m_ObjectText = value;
            }
        }
        public Location EditorLocation
        {
            get
            {
                return m_Location;
            }
            set
            {
                m_Location = value;
            }
        }
        #endregion
        
        #region IDrawable Members
        public virtual void Draw(System.Drawing.Graphics Graphics, System.Drawing.SizeF CharSize)
        { 
            
            float X = 0.0f;
            float Y = 0.0f;
            
            X = CharSize.Width * (m_Location.CharIndex-1);
            Y = CharSize.Height * (m_Location.LineNumber-1);
            
            Graphics.FillRectangle(new SolidBrush(this.BackgroundColor), X, Y, CharSize.Width * m_ObjectText.Length, CharSize.Height);
            
            Graphics.DrawString(m_ObjectText, new System.Drawing.Font("Parallax", 10.0f, (this.IsBold ? System.Drawing.FontStyle.Bold : FontStyle.Regular)), new System.Drawing.SolidBrush(this.TextColor), X, Y);
            
        }
        #endregion

        public static void RegisterParameters()
        {
            Properties.AddProperty(base.TypeName, new Properties.SpinProperty("TextColor", Color.Black));
            Properties.AddProperty(base.TypeName, new Properties.SpinProperty("IsBold", false));
            Properties.AddProperty(base.TypeName, new Properties.SpinProperty("BackgroundColor", Color.Black));
        }

    }
    #endregion Abstract Class SpingCodeObject
    
    #region Abstract Class SearchEngine
    public abstract class CodeSearchEngine : ICodeSearchEngine
    {
        
        private string m_RegexSearchString;
        private Regex m_SearchEngine;
        
        public CodeSearchEngine(string RegexSearch)
        {
            RegexSearchString = RegexSearch;
        }
        
        #region ICodeSearchEngine Members

        public abstract SpinCodeObject[] Search(string SearchStr, ref string SearchStream, ref int currentPos, ref int currentChar, ref int currentLine);
        
        public string RegexSearchString
        {
            get { return m_RegexSearchString; }
            set { m_RegexSearchString = value; m_SearchEngine = new Regex(m_RegexSearchString, RegexOptions.IgnoreCase); }
        }

        public Regex SearchEngine
        {
            get { return m_SearchEngine; }
        }

        #endregion
        
    }
    #endregion Abstract Clas SearchEngine
    
    #region Class SpinInterperter
    /// <summary>
    /// 
    /// SPINnet Compiler
    /// 
    /// Communication level to and from a .Spin file
    /// 
    /// actural Files on the file system needs to be
    /// backwards compatible with the official 
    /// Propeller Tool. This will make it possable
    /// to still share files on 
    /// 
    /// </summary>
    public class SpinInterpreter
    {
        
        #region Privates
        
        private string m_FilePath;
        private StringBuilder m_SpinData;
        private ArrayList m_SpinSearchEngines;
        private ArrayList m_SpinTypes;
        private ArrayList m_SpinObjects;
        
        #endregion Privates
        
        #region De/Constructor
        public SpinInterpreter(String SpinFilePath)
        {

            //Block.TextColor();

            m_SpinSearchEngines = new ArrayList();
            m_SpinObjects = new ArrayList();
            m_SpinTypes = new ArrayList();
            
            m_FilePath = SpinFilePath;
            
            BuildTypes();
            
            OpenFile();
            
            ParseFile();
            
        }
        ~SpinInterpreter()
        {
        }
        #endregion De/Constructor
        
        #region Private Functions
        private void BuildTypes()
        {
            
            Assembly enumASM = Assembly.GetCallingAssembly();
            
            foreach (Module tmpMod in enumASM.GetModules())
            {
                foreach (Type tmpType in tmpMod.GetTypes())
                {
                    if (tmpType.GetCustomAttributes(typeof(SearchEngineAttribute), true).Length > 0)
                    {
                        
                        string strType = tmpType.ToString();
                        Type tp = Assembly.GetCallingAssembly().GetType(strType);
                        object obj = Activator.CreateInstance(tp);
                        
                        m_SpinSearchEngines.Add(obj);
                        
                    }
                    else if (tmpType.GetCustomAttributes(typeof(SpinTypeAttribute), true).Length > 0)
                    {
                        
                        string strType = tmpType.ToString();
                        Type tp = Assembly.GetCallingAssembly().GetType(strType);
                        object obj = Activator.CreateInstance(tp);

                        foreach (MethodInfo method in tp.GetMethods())
                        {
                            foreach (object attribute in method.GetCustomAttributes(true))
                            {
                                if (attribute is SpinParameterAttribute)
                                {
                                    System.Diagnostics.Trace.WriteLine(attribute.ToString());
                                }
                            }

                        }


                        //PropellentNet.Properties.AddProperty(obj.ToString(), new Properties.SpinProperty("", null));
                        //m_SpinTypes.Add(obj);
                        
                    }
                }
            }
            
            
            
        }
        private void OpenFile()
        {
            
            m_SpinData = new StringBuilder();
            
            TextReader tmpRead = (TextReader)new StreamReader(m_FilePath);
            
            m_SpinData.Append(tmpRead.ReadToEnd());
            
            tmpRead.Close();

        }
        private void ParseFile()
        {

            DateTime startTime = DateTime.Now;

            string holdStr = m_SpinData.ToString();
            string searchStr = "";
            int currentChar = 0;
            int currentLine = 1;

            ArrayList ParentType = new ArrayList();
            //ParentType.Add( new SpinCodeObject("File", ""));
            
            for (int i = 0; i < holdStr.Length; i++)
            {
                
                searchStr += holdStr[i];

                currentChar += 1;
                
                foreach (CodeSearchEngine tmpEngine in m_SpinSearchEngines)
                {
                    
                    SpinCodeObject[] holdObj = tmpEngine.Search(searchStr, ref holdStr, ref i, ref currentChar, ref currentLine);
                      
                    if (holdObj != null)
                    {
                        m_SpinObjects.AddRange(holdObj);
                        searchStr = "";
                        break;
                    }
                    
                    /*
                    Match foundMatch = null;
                    
                    if ((foundMatch = tmpType.IsMatch(searchStr)).Success == true)
                    {
                        
                        string holdMatchValue = foundMatch.Value;

                        //if (holdMatchValue.IndexOf("if", 0, holdMatchValue.Length, StringComparison.OrdinalIgnoreCase) > -1)
                        //{
                        //    System.Diagnostics.Trace.WriteLine("sadf");
                        //}
                        
                        /// If we found a match but it doesn't start 
                        /// at the begining of the string. That means
                        /// we dont have a valid object before the current
                        /// match. We need to mark that section as such.
                        if (foundMatch.Index != 0)
                        {

                            /// Create new SpintCode Object based on 
                            /// the type that found the current match
                            SpinCodeObject tmpObj = null; // new SpinCodeObject((SpinCodeObject)ParentType[ParentType.Count - 1], ((sType)m_SpinTypes["Unknown"]).TypeName, ((sType)m_SpinTypes["Unknown"]).Regex);

                            /// set current object text = to the start of the
                            /// search string and end at the match start. 
                            tmpObj.ObjectText = searchStr.Substring(0, foundMatch.Index);

                            /// build a new text Location object with the current char and line 
                            Location tmploc = new Location();
                            tmploc.CharIndex = currentChar - searchStr.Length + 1;
                            tmploc.LineNumber = currentLine;
                            tmpObj.EditorLocation = tmploc;

                            /// Set Objects look and Feel = (Unknown Object) Type Look and Feel
                            tmpObj.BackgroundColor = ((sType)m_SpinTypes["Unknown"]).BackgroundColor;
                            tmpObj.IsBold = ((sType)m_SpinTypes["Unknown"]).TextBold;
                            tmpObj.TextColor = ((sType)m_SpinTypes["Unknown"]).TextColor;
                            
                            /// Add object to arraylist 
                            m_SpinObjects.Add(tmpObj);

                            /// Remove this preceding unknown object from the Match text
                            holdMatchValue = searchStr.Substring(foundMatch.Index, (searchStr.Length) - foundMatch.Index);
                            
                        }
                        
                        /// If the current SpintType that detected a match
                        /// isn't Text Formating then we build a code object
                        /// and add it to our main code object Arraylist
                        if (tmpType.TypeName != "TextFormating" && holdMatchValue != " ")
                        {
                            
                            //System.Diagnostics.Trace.WriteLine("Found it " + foundMatch.Value + "  -  " + tmpType.TypeName);

                            SpinCodeObject tmpObj = null; //  new SpinCodeObject((SpinCodeObject)ParentType[ParentType.Count - 1], tmpType.TypeName, tmpType.Regex);
                            tmpObj.ObjectText = holdMatchValue.Replace(" ", "");
                            Location tmploc = new Location();
                            tmploc.CharIndex = currentChar - (searchStr.Length - foundMatch.Index) + 1;
                            tmploc.LineNumber = currentLine;
                            tmpObj.EditorLocation = tmploc;
                            tmpObj.BackgroundColor = tmpType.BackgroundColor;
                            tmpObj.IsBold = tmpType.TextBold;
                            tmpObj.TextColor = tmpType.TextColor;
                            
                            m_SpinObjects.Add(tmpObj);
                            
                        }
                        else if (holdMatchValue == "\r\n")
                        {
                            currentChar = 0;
                            currentLine += 1;
                        }

                        if (holdMatchValue.Length < searchStr.Length - foundMatch.Index)
                        {
                            i -= holdMatchValue.Length + foundMatch.Index - 1;
                            currentChar -= holdMatchValue.Length + foundMatch.Index - 1;
                        }
                        searchStr = "";
                        
                        break;
                    }
                     */

                }
                
            }
            
            DateTime endTime = DateTime.Now;
            
            System.Diagnostics.Trace.WriteLine(((TimeSpan)endTime.Subtract(startTime)).TotalMilliseconds);
            
        }
        #endregion Private Functions
        
        #region Get/Set
        public ArrayList CodeObjects
        {
            get { return (ArrayList)m_SpinObjects.Clone(); }
        }
        public ArrayList SearchEngines
        {
            get { return (ArrayList)m_SpinSearchEngines; }
        }
        public ArrayList SpinTypes
        {
            get { return (ArrayList)m_SpinTypes; }
        }
        #endregion Get/Set
        
        #region Public Functiosn
         
        #endregion Public Functions
        
    }
    #endregion Class SpinInterperter
    
}
